package com.lagou.ipersistence.session;

import com.lagou.ipersistence.binding.MapperRegistry;
import com.lagou.ipersistence.executor.Executor;
import com.lagou.ipersistence.executor.SimpleExecutor;
import com.lagou.ipersistence.executor.parameter.ParameterHandler;
import com.lagou.ipersistence.executor.resultset.DefaultResultSetHandler;
import com.lagou.ipersistence.executor.resultset.ResultSetHandler;
import com.lagou.ipersistence.executor.statement.RoutingStatementHandler;
import com.lagou.ipersistence.executor.statement.StatementHandler;
import com.lagou.ipersistence.mapping.BoundSql;
import com.lagou.ipersistence.mapping.Environment;
import com.lagou.ipersistence.mapping.MappedStatement;
import com.lagou.ipersistence.parsing.XNode;
import com.lagou.ipersistence.reflection.DefaultReflectorFactory;
import com.lagou.ipersistence.reflection.MetaObject;
import com.lagou.ipersistence.reflection.ReflectorFactory;
import com.lagou.ipersistence.reflection.factory.DefaultObjectFactory;
import com.lagou.ipersistence.reflection.factory.ObjectFactory;
import com.lagou.ipersistence.reflection.wrapper.DefaultObjectWrapperFactory;
import com.lagou.ipersistence.reflection.wrapper.ObjectWrapperFactory;
import com.lagou.ipersistence.scripting.LanguageDriver;
import com.lagou.ipersistence.scripting.LanguageDriverRegistry;
import com.lagou.ipersistence.scripting.xmltags.XMLLanguageDriver;
import com.lagou.ipersistence.transaction.Transaction;
import com.lagou.ipersistence.type.TypeAliasRegistry;
import com.lagou.ipersistence.type.TypeHandlerRegistry;

import javax.sql.DataSource;
import java.util.*;

/**
 * @author Yan Jiahong
 * Created on 2020/11/15
 */
public class Configuration {
    protected Environment environment;
    protected Properties variables = new Properties();
    protected DataSource dataSource;
    protected Map<String, MappedStatement> mappedStatementMap = new HashMap<>();
    protected Map<String, XNode> sqlFragments = new HashMap<>();
    protected TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    protected ObjectFactory objectFactory = new DefaultObjectFactory();
    protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
    protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    protected TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
    protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
    protected LanguageDriverRegistry languageDriverRegistry = new LanguageDriverRegistry();
    protected MapperRegistry mapperRegistry = new MapperRegistry(this);
    protected Set<String> loadedResources = new HashSet<>();
    protected Integer defaultStatementTimeout;
    
    public Configuration() {
        languageDriverRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    }
    
    public Environment getEnvironment() {
        return environment;
    }
    
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
    
    public Properties getVariables() {
        return variables;
    }
    
    public void setVariables(Properties variables) {
        this.variables = variables;
    }
    
    public DataSource getDataSource() {
        return dataSource;
    }
    
    public Map<String, XNode> getSqlFragments() {
        return sqlFragments;
    }
    
    public void setSqlFragments(Map<String, XNode> sqlFragments) {
        this.sqlFragments = sqlFragments;
    }
    
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    public Map<String, MappedStatement> getMappedStatementMap() {
        return mappedStatementMap;
    }
    
    public void setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) {
        this.mappedStatementMap = mappedStatementMap;
    }
    
    public TypeAliasRegistry getTypeAliasRegistry() {
        return typeAliasRegistry;
    }
    
    public void setTypeAliasRegistry(TypeAliasRegistry typeAliasRegistry) {
        this.typeAliasRegistry = typeAliasRegistry;
    }
    
    public ReflectorFactory getReflectorFactory() {
        return reflectorFactory;
    }
    
    public void addMappedStatement(MappedStatement ms) {
        mappedStatementMap.put(ms.getId(), ms);
    }
    
    public boolean hasStatement(String statementId) {
        return mappedStatementMap.containsKey(statementId);
    }
    
    public MappedStatement getMappedStatement(String statementId) {
        return mappedStatementMap.get(statementId);
    }
    
    public ObjectFactory getObjectFactory() {
        return objectFactory;
    }
    
    public Executor newExecutor(ExecutorType execType, Transaction transaction) {
        return new SimpleExecutor(this, transaction);
    }
    
    public ExecutorType getDefaultExecutorType() {
        return defaultExecutorType;
    }
    
    public TypeHandlerRegistry getTypeHandlerRegistry() {
        return typeHandlerRegistry;
    }
    
    public MetaObject newMetaObject(Object object) {
        return MetaObject.froObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    }
    
    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject,
                                                RowBounds rowBounds, ResultHandler<?> resultHandler, BoundSql boundSql) {
        return new RoutingStatementHandler(executor, mappedStatement, parameterObject,
                rowBounds, resultHandler, boundSql);
    }
    
    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject,
                                                BoundSql boundSql) {
        return mappedStatement.getLang().createParameterHandler(mappedStatement,
                parameterObject, boundSql);
    }
    
    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds,
                                                ParameterHandler parameterHandler, ResultHandler<?> resultHandler,
                                                BoundSql boundSql) {
        return new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler,
                boundSql, rowBounds);
    }
    
    public LanguageDriver getDefaultScriptingLanguageInstance() {
        return languageDriverRegistry.getDefaultDriver();
    }
    
    public Integer getDefaultStatementTimeout() {
        return defaultStatementTimeout;
    }
    
    public void setDefaultStatementTimeout(Integer defaultStatementTimeout) {
        this.defaultStatementTimeout = defaultStatementTimeout;
    }
    
    public boolean hasMapper(Class<?> type) {
        return mapperRegistry.hasMapper(type);
    }
    
    public void addLoadedResources(String resource) {
        loadedResources.add(resource);
    }
    
    public <T> void addMapper(Class<T> type) {
        mapperRegistry.addMapper(type);
    }
    
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mapperRegistry.getMapper(type, sqlSession);
    }
}
